﻿using Core.DataAccess.Model;
using Core.Framework;
using Dapper;
using Grain.Interfaces;
using Grain.State.BaseModule.StorgeStates;
using iTool.ClusterComponent;
using Orleans;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;

namespace Grain.Implements
{
    public class ChannelService : iToolServiceBase<ChannelServiceState>, IChannelService
    {

        private IDBFactory iDBFactory;

        public ChannelService(IDBFactory iDBFactory)
        {
            this.iDBFactory = iDBFactory;
        }

        #region 订阅管理部分
        public Task<List<string>> GetSubscribes()
        {
            return Task.FromResult(this.State.Topic.Subscribes);
        }

        public async Task Subscribe(string topic)
        {

            var iNotify = this.GrainFactory.GetGrain<INotifyService>(topic);
            await iNotify.Subscribe(this.GetPrimaryKeyString());

            if (!this.State.Topic.Subscribes.Contains(topic))
            {
                this.State.Topic.Subscribes.Add(topic);
                await this.WriteStateAsync();
            }
        }

        public async Task UnSubscribe(string topic)
        {
            if (this.State.Topic.Subscribes.Contains(topic))
            {
                var iNotify = this.GrainFactory.GetGrain<INotifyService>(topic);
                await iNotify.UnSubscribe(this.GetPrimaryKeyString());

                this.State.Topic.Subscribes.Remove(topic);
                await this.WriteStateAsync();
            }

        }

        #endregion

        #region 渠道信息部分

        public Task SetChannelSesstion(string parameter)
        {
            this.State.ChannelSession = parameter;
            return Task.CompletedTask;
            // sesstion 不需要持久
            // return this.WriteStateAsync();
        }

        public Task<string> GetChannelSesstion()
        {
            return Task.FromResult(this.State.ChannelSession);
        }

        public Task SetChannelInfo(string parameter)
        {
            this.State.ChannelInfo = parameter;
            return this.WriteStateAsync();
        }

        public Task<string> GetChannelInfo()
        {
            return Task.FromResult(this.State.ChannelInfo);
        }

        #endregion

        #region 会话处理部分

        public async Task ReceiveMessageEvent(string message, string sendChannel, string sendParameter)
        {
            Sesstion sesstion = new Sesstion();

            // 构造会话信息
            if (this.State.SesstionList.ContainsKey(sendChannel))
            {
                this.State.SesstionList.Remove(sendChannel, out sesstion);
                sesstion.Count = sesstion.IsIntoChannel ? 0 : (sesstion.Count + 1);
            }
            else
            {
                sesstion.Count = 1;
            }

            // message 其实是可以格式化的（暂时不考虑）
            sesstion.UserParameter = sendParameter;
            sesstion.Message = message;
            sesstion.Time = DateTime.Now;

            this.State.SesstionList.Add(sendChannel, sesstion);
            await this.WriteStateAsync();
        }

        public async Task SendMessageEvent(string message, string receiveChannel, string receiveParameter)
        {
            Sesstion sesstion = new Sesstion();

            // 构造会话信息
            if (this.State.SesstionList.ContainsKey(receiveChannel))
            {
                this.State.SesstionList.Remove(receiveChannel, out sesstion);
            }

            // message 其实是可以格式化的（暂时不考虑）
            sesstion.UserParameter = receiveParameter;
            sesstion.Count = 0;
            sesstion.Message = message;
            sesstion.Time = DateTime.Now;

            this.State.SesstionList.Add(receiveChannel, sesstion);
            await this.WriteStateAsync();
        }

        public async Task ReaderChannel(string receiveChannel)
        {
            if (this.State.SesstionList.ContainsKey(receiveChannel))
            {
                var sesstion = this.State.SesstionList[receiveChannel];
                sesstion.Count = 0;
                await this.WriteStateAsync();
            }
        }

        public Task<bool> IsInChannel(string channel)
        {
            if (this.State.SesstionList.ContainsKey(channel))
            {
                return Task.FromResult(this.State.SesstionList[channel].IsIntoChannel);
            }

            return Task.FromResult(false);
        }

        public async Task<KeyValuePair<bool, string>> IntoChannel(string receiveChannel)
        {
            var iReceiveChannel = this.GrainFactory.GetGrain<IChannelService>(receiveChannel);
            bool isReciveInSession = await iReceiveChannel.IsInChannel(this.GetPrimaryKeyString());
            string receiveParameter = await iReceiveChannel.GetChannelInfo();


            if (this.State.SesstionList.ContainsKey(receiveChannel))
            {
                var filter = this.State.SesstionList.Where(item => item.Value.IsIntoChannel);

                foreach (var item in this.State.SesstionList)
                {
                    if (item.Value?.IsIntoChannel == true & item.Key == receiveChannel)
                    {
                        return await Task.FromResult(new KeyValuePair<bool, string>(isReciveInSession, receiveParameter));
                    }
                    else if (item.Value?.IsIntoChannel == true)
                    {
                        item.Value.IsIntoChannel = false;
                        break;
                    }
                }

                var sesstion = this.State.SesstionList[receiveChannel];
                sesstion.Count = 0;
                sesstion.IsIntoChannel = true;
                await this.WriteStateAsync();

                // Notify和Channel  需要优化太多冲突的点
                await this.NotifySilence(receiveChannel, sesstion.IsIntoChannel);
            }

            return await Task.FromResult(new KeyValuePair<bool, string>(isReciveInSession, receiveParameter));

        }

        public async Task OutChannel(string channel)
        {
            if (this.State.SesstionList.ContainsKey(channel))
            {
                var sesstion = this.State.SesstionList[channel];
                if (sesstion.IsIntoChannel)
                {
                    sesstion.IsIntoChannel = false;
                    await this.WriteStateAsync();
                    await this.NotifySilence(channel, sesstion.IsIntoChannel);
                }
            }
        }

        public Task RemoveSesstionByChannel(string sendOfChannel)
        {
            if (this.State.SesstionList.ContainsKey(sendOfChannel))
            {
                this.State.SesstionList.Remove(sendOfChannel);
                this.WriteStateAsync();
            }

            return Task.CompletedTask;
        }

        #endregion

        #region 上下线管理

        public async Task GoOnline(string service, string room = null)
        {
            if (!this.State.Onlines.Contains(service))
            {
                this.State.Onlines.Add(service);
            }

            if (!string.IsNullOrWhiteSpace(room))
            {
                if (this.State.SesstionList.ContainsKey(room))
                {
                    var sesstion = this.State.SesstionList[room];
                    sesstion.Count = 0;
                    sesstion.IsIntoChannel = true;
                }
            }

            await this.Subscribe(this.GetPrimaryKeyString());

            await this.WriteStateAsync();
        }

        public async Task Offline(string service)
        {
            if (this.State.Onlines.Contains(service))
            {
                this.State.Onlines.Remove(service);
            }

            var sesstion = this.State.SesstionList.Where(item => item.Value.IsIntoChannel).FirstOrDefault();
            if (!string.IsNullOrWhiteSpace(sesstion.Key))
            {
                var room = this.State.SesstionList[sesstion.Key];
                room.IsIntoChannel = false;
            }

            //await this.UnSubscribe(this.GetPrimaryKeyString());

            await this.WriteStateAsync();
        }

        public Task<bool> IsOnline()
        {
            bool isOnlines = this.State.Onlines.Count > 0;
            return Task.FromResult(isOnlines);
        }

        #endregion

        #region 消息管理

        // TUDO 优化方案（需要详细拆分此Grain，进行重新设计）V1：暂不处理
        // 群组消息和一对一消息分开存储
        // 群组可以缓存第一页消息


        public Task ModifyMessageStatus(DBNotifyMessage message)
        {
            using (var db = this.iDBFactory.Create(DatabaseType.Message))
            {
                var rows = db.Execute($"update NotifyMessage set [status] = {message.status} where [id] = {message.id}");
            }
            return Task.CompletedTask;
        }

        public Task<Pagination> GetMessage(Pagination pagination, string reciveChannel, DateTime minDateTime)
        {
            var selfChannle = this.GetPrimaryKeyString();
            pagination.rows = pagination.rows > 0 ? pagination.rows : 10;
            pagination.page = pagination.page == 0 ? 1 : pagination.page;

            using (var db = this.iDBFactory.Create(DatabaseType.Message))
            {
                try
                {
                    pagination.records = db.RecordCount<DBNotifyMessage>
                        ("where (reciveChannel = @reciveChannel and sendChannel = @sendChannel or reciveChannel = @sendChannel and sendChannel = @reciveChannel) and createTime < @minDateTime", parameters: new
                        {
                            minDateTime,
                            reciveChannel,
                            sendChannel = selfChannle
                        });

                    if (pagination.records > ((pagination.page - 1) * pagination.rows))
                    {
                        pagination.Data = db.GetListPaged<DBNotifyMessage>(
                            pagination.page,
                            pagination.rows,
                            "where (reciveChannel = @reciveChannel and sendChannel = @sendChannel or reciveChannel = @sendChannel and sendChannel = @reciveChannel) and createTime < @minDateTime",
                            $"id desc",
                            new
                            {
                                minDateTime,
                                reciveChannel,
                                sendChannel = selfChannle
                            })
                            .ToList();

                        var ids = (pagination.Data as List<DBNotifyMessage>).Where(item => item.status == 0 && item.sendChannel != selfChannle).Select(item => item.id);
                        this.ModifyReaderMessage(ids, db);

                        foreach (var item in pagination.Data as List<DBNotifyMessage>)
                        {
                            if (item.sendChannel == selfChannle)
                            {
                                item.sendChannel = null;
                            }
                        }

                    }
                }
                catch (Exception ex)
                {
                    pagination.Data = ex.Message;
                }
            }

            return Task.FromResult(pagination);

        }

        public Task<Pagination> GetMessage(Pagination pagination, string reciveChannel, DateTime minDateTime, DateTime minHistoryDateTime)
        {

            pagination.rows = 10;
            pagination.page = pagination.page == 0 ? 1 : pagination.page;
            var selfChannle = this.GetPrimaryKeyString();

            using (var db = this.iDBFactory.Create(DatabaseType.Message))
            {
                try
                {
                    pagination.records = db.RecordCount<DBNotifyMessage>("where (reciveChannel = @reciveChannel and sendChannel = @sendChannel or reciveChannel = @sendChannel and sendChannel = @reciveChannel) and createTime < @minDateTime and createTime >= @minHistoryDateTime",
                        parameters: new
                        {
                            minDateTime,
                            minHistoryDateTime = minHistoryDateTime.AddSeconds(1),
                            reciveChannel,
                            sendChannel = selfChannle
                        });

                    if (pagination.records > ((pagination.page - 1) * pagination.rows))
                    {
                        pagination.Data = db.GetListPaged<DBNotifyMessage>(
                            pagination.page,
                            pagination.rows,
                            "where (reciveChannel = @reciveChannel and sendChannel = @sendChannel or reciveChannel = @sendChannel and sendChannel = @reciveChannel) and createTime < @minDateTime and createTime >= @minHistoryDateTime",
                            $"id desc",
                            new
                            {
                                minDateTime,
                                reciveChannel,
                                minHistoryDateTime = minHistoryDateTime.AddSeconds(1),
                                sendChannel = this.GetPrimaryKeyString()
                            })
                            .ToList();

                        var ids = (pagination.Data as List<DBNotifyMessage>).Where(item => item.status == 0 && item.sendChannel != selfChannle).Select(item => item.id);
                        this.ModifyReaderMessage(ids, db);

                        foreach (var item in pagination.Data as List<DBNotifyMessage>)
                        {
                            if (item.sendChannel == selfChannle)
                            {
                                item.sendChannel = null;
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    pagination.Data = ex.Message;
                }
            }

            return Task.FromResult(pagination);

        }

        public Task<Pagination> GetMessageOfGroup(Pagination pagination, string reciveChannel, DateTime minDateTime)
        {

            pagination.rows = 10;
            pagination.page = pagination.page == 0 ? 1 : pagination.page;
            var selfChannle = this.GetPrimaryKeyString();

            using (var db = this.iDBFactory.Create(DatabaseType.Message))
            {
                try
                {
                    pagination.records = db.RecordCount<DBNotifyMessage>("where reciveChannel = @reciveChannel and createTime < @minDateTime", parameters: new
                    {
                        minDateTime,
                        reciveChannel,
                        sendChannel = selfChannle
                    });

                    if (pagination.records > ((pagination.page - 1) * pagination.rows))
                    {
                        pagination.Data = db.GetListPaged<DBNotifyMessage>(
                            pagination.page,
                            pagination.rows,
                            "where reciveChannel = @reciveChannel and createTime < @minDateTime",
                            $"id desc",
                            new
                            {
                                minDateTime,
                                reciveChannel,
                                sendChannel = this.GetPrimaryKeyString()
                            })
                            .ToList();

                        foreach (var item in pagination.Data as List<DBNotifyMessage>)
                        {
                            if (item.sendChannel == selfChannle)
                            {
                                item.sendChannel = null;
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    pagination.Data = ex.Message;
                }
            }

            return Task.FromResult(pagination);

        }

        public Task<Pagination> GetMessageOfGroup(Pagination pagination, string reciveChannel, DateTime minDateTime, DateTime minHistoryDateTime)
        {

            pagination.rows = 10;
            pagination.page = pagination.page == 0 ? 1 : pagination.page;
            var selfChannle = this.GetPrimaryKeyString();

            using (var db = this.iDBFactory.Create(DatabaseType.Message))
            {
                try
                {
                    pagination.records = db.RecordCount<DBNotifyMessage>("where reciveChannel = @reciveChannel and createTime < @minDateTime and createTime > @minHistoryDateTime", parameters: new
                    {
                        minDateTime,
                        minHistoryDateTime,
                        reciveChannel,
                        sendChannel = this.GetPrimaryKeyString()
                    });

                    if (pagination.records > ((pagination.page - 1) * pagination.rows))
                    {
                        pagination.Data = db.GetListPaged<DBNotifyMessage>(
                            pagination.page,
                            pagination.rows,
                            "where reciveChannel = @reciveChannel and createTime < @minDateTime and createTime > @minHistoryDateTime",
                            $"id desc",
                            new
                            {
                                minDateTime,
                                reciveChannel,
                                minHistoryDateTime,
                                sendChannel = selfChannle
                            })
                            .ToList();

                        foreach (var item in pagination.Data as List<DBNotifyMessage>)
                        {
                            if (item.sendChannel == selfChannle)
                            {
                                item.sendChannel = null;
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    pagination.Data = ex.Message;
                }
            }

            return Task.FromResult(pagination);

        }

        public Task<Dictionary<string, Sesstion>> GetSesstion(DateTime? lastDateTime)
        {
            if (lastDateTime == null)
            {
                return Task.FromResult(this.State.SesstionList);
            }
            else
            {
                var isChnaged = this.State.SesstionList.Values.Select(item => item.Time).Max() != lastDateTime;
                if (isChnaged)
                {
                    return Task.FromResult(this.State.SesstionList);
                }
                else
                {
                    return Task.FromResult<Dictionary<string, Sesstion>>(default);
                }
            }
        }


        #endregion


        private void ModifyReaderMessage(IEnumerable<int> ids, IDbConnection db)
        {
            if (ids.Count() > 0)
            {
                db.Execute($"update NotifyMessage set status = 1 where id in (${string.Join(',', ids)})");
            }
        }

        private async Task NotifySilence(string channel, bool isIn)
        {
            var iNotify = this.GrainFactory.GetGrain<INotifyService>(channel);
            await iNotify.PublishSilence(this.GetPrimaryKeyString(), new
            {
                type = isIn ? "1_silence" : "0_silence",
                channel = channel

            }.TryToJson());
        }
    }
}
